﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media.Imaging;

namespace VRacer
{
    internal struct drawingPoint
    {
        public int x, y, color;
        public drawingPoint(int x, int y, int color)
        {
            this.x = x;
            this.y = y;
            this.color = color;
        }
    }

    public static class AIDraw
    {
        static private Dictionary<Track, WriteableBitmap> _tracks = new Dictionary<Track, WriteableBitmap>();
        static Queue<drawingPoint> kolejka = new Queue<drawingPoint>();

        /// <summary>
        /// Jeśli nowa mapa została dodana to przygotowuje ją do pokolorowania
        /// </summary>
        /// <param name="mapa"></param>
        public static void Add(Track mapa)
        {
            if (!_tracks.ContainsKey(mapa))
            {
                _tracks.Add(mapa, mapa.Bitmap.CloneCurrentValue());
                //możliwa zmiana na ponowne wczytanie tj. w tracku
                foreach (Point slot in mapa.StartSlots)
                {
                    kolejka.Enqueue(new drawingPoint((int)slot.X, (int)slot.Y, (100 * 256 + 100) * 256 + 100));
                }
                _pokoloruj();
            }
        }
        /// <summary>
        /// Metoda ta koloruje mapke zaczynająć od mety i idzie do startu, od kolurów ciemnych do jasnych
        /// </summary>
        static private void _pokoloruj()
        {
            int color = (100 * 256 + 100) * 256 + 99, r = 100, g = 100, b = 99;
            Track currentTrack = _tracks.Last().Key;
            drawingPoint dp;
            while (kolejka.Count > 0)   //dla każdego z listy
            {
                while (kolejka.Count > 0 && kolejka.First().color != color)
                {
                    dp = kolejka.Dequeue();

                    if (CheckWhite(dp.x, dp.y, currentTrack))
                    {
                        //pokoloruj go na aktualny kolor
                        SetColor(dp.x, dp.y, currentTrack, color);
                        //dodaj wszystkich jego sąsiadów na nową liste
                        if (currentTrack.GetCollisionPoint((double)dp.x, (double)dp.y, (double)(dp.x + 1), (double)dp.y).X == -1
                            && currentTrack.GetFinishCrossPoint((double)dp.x, (double)dp.y, (double)(dp.x + 1), (double)dp.y).X == -1)
                            kolejka.Enqueue(new drawingPoint(dp.x + 1, dp.y, color));
                        if (currentTrack.GetCollisionPoint((double)dp.x, (double)dp.y, (double)(dp.x - 1), (double)dp.y).X == -1
                            && currentTrack.GetFinishCrossPoint((double)dp.x, (double)dp.y, (double)(dp.x - 1), (double)dp.y).X == -1)
                            kolejka.Enqueue(new drawingPoint(dp.x - 1, dp.y, color));
                        if (currentTrack.GetCollisionPoint((double)dp.x, (double)dp.y, (double)dp.x, (double)(dp.y + 1)).X == -1
                            && currentTrack.GetFinishCrossPoint((double)dp.x, (double)dp.y, (double)dp.x, (double)(dp.y + 1)).X == -1)
                            kolejka.Enqueue(new drawingPoint(dp.x, dp.y + 1, color));
                        if (currentTrack.GetCollisionPoint((double)dp.x, (double)dp.y, (double)dp.x, (double)(dp.y - 1)).X == -1
                            && currentTrack.GetFinishCrossPoint((double)dp.x, (double)dp.y, (double)dp.x, (double)(dp.y - 1)).X == -1)
                            kolejka.Enqueue(new drawingPoint(dp.x, dp.y - 1, color));
                    }
                }
                //zmienia aktualny kolor na następny
                b--;
                if (b == 0)
                {
                    b += 100;
                    g--;
                    if (g == 0)
                        g += 100;
                    r--;
                }
                color = (((r << 8) + g) << 8) + b;
            }

        }

        /// <summary>
        /// sprawdza czy pixel jest biały - możliw do zakolorowania
        /// </summary>
        /// <param name="x">Pozycja X</param>
        /// <param name="y">Pozycja Y</param>
        /// <param name="CurrentTrack">Aktualny track</param>
        /// <returns></returns>
        private static bool CheckWhite(int x, int y, Track CurrentTrack)
        {
            x = (int)(x * CurrentTrack.Bitmap.PixelHeight / CurrentTrack.Height);
            y = (int)(y * CurrentTrack.Bitmap.PixelWidth / CurrentTrack.Width);
            unsafe
            {
                int* buffer = (int*)_tracks[CurrentTrack].BackBuffer;
                buffer += x;
                buffer += y * CurrentTrack.Bitmap.BackBufferStride / 4;

                int color = *buffer;

                byte red = (byte)(color >> 16);
                byte green = (byte)(color >> 8);
                byte blue = (byte)(color >> 0);

                if (red > 128 && green > 128 && blue > 128)
                    return true;
                return false;
            }
        }

        /// <summary>
        /// Ustawia kolor danego pixela
        /// </summary>
        /// <param name="x">Pozycja X</param>
        /// <param name="y">Pozycja Y</param>
        /// <param name="CurrentTrack">Aktualny track</param>
        /// <param name="color"></param>
        private static void SetColor(int x, int y, Track CurrentTrack, int color)
        {
            x = (int)(x * CurrentTrack.Bitmap.PixelHeight / CurrentTrack.Height);
            y = (int)(y * CurrentTrack.Bitmap.PixelWidth / CurrentTrack.Width);
            unsafe
            {
                int* buffer = (int*)_tracks[CurrentTrack].BackBuffer;
                buffer += x;
                buffer += y * CurrentTrack.Bitmap.BackBufferStride / 4;
                *buffer = color;
            }
        }

        /// <summary>
        /// Pobiera kolor danego pixela
        /// </summary>
        /// <param name="x">Pozycja X</param>
        /// <param name="y">Pozycja Y</param>
        /// <param name="CurrentTrack">Aktualny track</param>
        /// <returns></returns>
        public static int GetColor(int x, int y, Track CurrentTrack)
        {
            x = (int)(x * CurrentTrack.Bitmap.PixelHeight / CurrentTrack.Height);
            y = (int)(y * CurrentTrack.Bitmap.PixelWidth / CurrentTrack.Width);
            if (x >= 0 && x < CurrentTrack.Bitmap.PixelHeight && y >= 0 && y < CurrentTrack.Bitmap.PixelWidth)
            {
                unsafe
                {
                    int* buffer = (int*)_tracks[CurrentTrack].BackBuffer;
                    buffer += x;
                    buffer += y * CurrentTrack.Bitmap.BackBufferStride / 4;
                    return *buffer;
                }
            }
            return 0;
        }
    }
}
